﻿/*
 * 2021.03.27-Code rectification and function enhancement.
 *            Huawei Device Co., Ltd. <mobile@huawei.com>
 */

#include "../StdAfx.h"
#include <atlcomcli.h>
#include "../Debug/LogOut.h"
namespace DuiLib {

class CActiveXCtrl;
class CActiveXWnd : public CWindowWnd {
public:
    CActiveXWnd() : m_bDrawCaret(false), m_pOwner(nullptr) {}
    ~CActiveXWnd() {}
    HWND Init(CActiveXCtrl *pOwner, HWND hWndParent);

    LPCTSTR GetWindowClassName() const;
    void OnFinalMessage(HWND hWnd);

    LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);

protected:
    void DoVerb(LONG iVerb);

    LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
    LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
    LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
    LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
    LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
    LRESULT OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
    LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
    LRESULT OnPrint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);

protected:
    CActiveXCtrl *m_pOwner;
    bool m_bDrawCaret;
};

class CActiveXEnum : public IEnumUnknown {
public:
    explicit CActiveXEnum(IUnknown *pUnk) : m_pUnk(pUnk), m_dwRef(1), m_iPos(0)
    {
        if (m_pUnk == nullptr) {
            return;
        }
        m_pUnk->AddRef();
    }
    ~CActiveXEnum()
    {
        if (m_pUnk == nullptr) {
            return;
        }
        m_pUnk->Release();
    }

    LONG m_iPos;
    ULONG m_dwRef;
    IUnknown *m_pUnk;

    STDMETHOD_(ULONG, AddRef)()
    {
        return ++m_dwRef;
    }
    STDMETHOD_(ULONG, Release)()
    {
        LONG lRef = --m_dwRef;
        if (lRef == 0) {
            delete this;
        }
        return lRef;
    }
    STDMETHOD(QueryInterface)(REFIID riid, LPVOID *ppvObject)
    {
        if (ppvObject == nullptr) {
            return E_NOINTERFACE;
        }
        *ppvObject = nullptr;
        if (riid == IID_IUnknown) {
            *ppvObject = static_cast<IEnumUnknown *>(this);
        } else if (riid == IID_IEnumUnknown) {
            *ppvObject = static_cast<IEnumUnknown *>(this);
        }
        if (*ppvObject != nullptr) {
            AddRef();
        }
        return *ppvObject == nullptr ? E_NOINTERFACE : S_OK;
    }
    STDMETHOD(Next)(ULONG celt, IUnknown **rgelt, ULONG *pceltFetched)
    {
        if (pceltFetched != nullptr) {
            *pceltFetched = 0;
        }
        if (++m_iPos > 1) {
            return S_FALSE;
        }
        if (rgelt == nullptr) {
            return E_NOINTERFACE;
        }
        *rgelt = m_pUnk;
        (*rgelt)->AddRef();
        if (pceltFetched != nullptr) {
            *pceltFetched = 1;
        }
        return S_OK;
    }
    STDMETHOD(Skip)(ULONG celt)
    {
        m_iPos += celt;
        return S_OK;
    }
    STDMETHOD(Reset)(void)
    {
        m_iPos = 0;
        return S_OK;
    }
    STDMETHOD(Clone)(IEnumUnknown **ppenum)
    {
        return E_NOTIMPL;
    }
};

class CActiveXFrameWnd : public IOleInPlaceFrame {
public:
    explicit CActiveXFrameWnd(CActiveXUI *pOwner) : m_dwRef(1), m_pOwner(pOwner), m_pActiveObject(nullptr) {}
    ~CActiveXFrameWnd()
    {
        if (m_pActiveObject != nullptr) {
            m_pActiveObject->Release();
        }
    }

    ULONG m_dwRef;
    CActiveXUI *m_pOwner;
    IOleInPlaceActiveObject *m_pActiveObject;

    // IUnknown
    STDMETHOD_(ULONG, AddRef)()
    {
        return ++m_dwRef;
    }
    STDMETHOD_(ULONG, Release)()
    {
        ULONG lRef = --m_dwRef;
        if (lRef == 0) {
            delete this;
        }
        return lRef;
    }
    STDMETHOD(QueryInterface)(REFIID riid, LPVOID *ppvObject)
    {
        if (ppvObject == nullptr) {
            return E_POINTER;
        }
        *ppvObject = nullptr;
        if (riid == IID_IUnknown) {
            *ppvObject = static_cast<IOleInPlaceFrame *>(this);
        } else if (riid == IID_IOleWindow) {
            *ppvObject = static_cast<IOleWindow *>(this);
        } else if (riid == IID_IOleInPlaceFrame) {
            *ppvObject = static_cast<IOleInPlaceFrame *>(this);
        } else if (riid == IID_IOleInPlaceUIWindow) {
            *ppvObject = static_cast<IOleInPlaceUIWindow *>(this);
        }
        if (*ppvObject != nullptr) {
            AddRef();
        }
        return *ppvObject == nullptr ? E_NOINTERFACE : S_OK;
    }
    // IOleInPlaceFrameWindow
    STDMETHOD(InsertMenus)(HMENU, LPOLEMENUGROUPWIDTHS)
    {
        return S_OK;
    }
    STDMETHOD(SetMenu)(HMENU, HOLEMENU, HWND)
    {
        return S_OK;
    }
    STDMETHOD(RemoveMenus)(HMENU)
    {
        return S_OK;
    }
    STDMETHOD(SetStatusText)(LPCOLESTR)
    {
        return S_OK;
    }
    STDMETHOD(EnableModeless)(BOOL)
    {
        return S_OK;
    }
    STDMETHOD(TranslateAccelerator)(LPMSG, WORD)
    {
        return S_FALSE;
    }
    // IOleWindow
    STDMETHOD(GetWindow)(HWND *phwnd)
    {
        if (phwnd == nullptr || m_pOwner == nullptr) {
            return E_UNEXPECTED;
        }
        *phwnd = m_pOwner->GetManager()->GetPaintWindow();
        return S_OK;
    }
    STDMETHOD(ContextSensitiveHelp)(BOOL)
    {
        return S_OK;
    }
    // IOleInPlaceUIWindow
    STDMETHOD(GetBorder)(LPRECT)
    {
        return S_OK;
    }
    STDMETHOD(RequestBorderSpace)(LPCBORDERWIDTHS)
    {
        return INPLACE_E_NOTOOLSPACE;
    }
    STDMETHOD(SetBorderSpace)(LPCBORDERWIDTHS)
    {
        return S_OK;
    }
    STDMETHOD(SetActiveObject)(IOleInPlaceActiveObject *pActiveObject, LPCOLESTR)
    {
        if (pActiveObject != nullptr) {
            pActiveObject->AddRef();
        }
        if (m_pActiveObject != nullptr) {
            m_pActiveObject->Release();
        }
        m_pActiveObject = pActiveObject;
        return S_OK;
    }
};

class CActiveXCtrl : public IOleClientSite,
    public IOleInPlaceSiteWindowless,
    public IOleControlSite,
    public IObjectWithSite,
    public IOleContainer {
    friend class CActiveXUI;
    friend class CActiveXWnd;

public:
    CActiveXCtrl();
    ~CActiveXCtrl();

    // IUnknown
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();
    STDMETHOD(QueryInterface)(REFIID riid, LPVOID *ppvObject);

    // IObjectWithSite
    STDMETHOD(SetSite)(IUnknown *pUnkSite);
    STDMETHOD(GetSite)(REFIID riid, LPVOID *ppvSite);

    // IOleClientSite
    STDMETHOD(SaveObject)(void);
    STDMETHOD(GetMoniker)(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker **ppmk);
    STDMETHOD(GetContainer)(IOleContainer **ppContainer);
    STDMETHOD(ShowObject)(void);
    STDMETHOD(OnShowWindow)(BOOL fShow);
    STDMETHOD(RequestNewObjectLayout)(void);

    // IOleInPlaceSiteWindowless
    STDMETHOD(CanWindowlessActivate)(void);
    STDMETHOD(GetCapture)(void);
    STDMETHOD(SetCapture)(BOOL fCapture);
    STDMETHOD(GetFocus)(void);
    STDMETHOD(SetFocus)(BOOL fFocus);
    STDMETHOD(GetDC)(LPCRECT pRect, DWORD grfFlags, HDC *phDC);
    STDMETHOD(ReleaseDC)(HDC hDC);
    STDMETHOD(InvalidateRect)(LPCRECT pRect, BOOL fErase);
    STDMETHOD(InvalidateRgn)(HRGN hRGN, BOOL fErase);
    STDMETHOD(ScrollRect)(INT dx, INT dy, LPCRECT pRectScroll, LPCRECT pRectClip);
    STDMETHOD(AdjustRect)(LPRECT prc);
    STDMETHOD(OnDefWindowMessage)(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *plResult);

    // IOleInPlaceSiteEx
    STDMETHOD(OnInPlaceActivateEx)(BOOL *pfNoRedraw, DWORD dwFlags);
    STDMETHOD(OnInPlaceDeactivateEx)(BOOL fNoRedraw);
    STDMETHOD(RequestUIActivate)(void);

    // IOleInPlaceSite
    STDMETHOD(CanInPlaceActivate)(void);
    STDMETHOD(OnInPlaceActivate)(void);
    STDMETHOD(OnUIActivate)(void);
    STDMETHOD(GetWindowContext)
    (IOleInPlaceFrame **ppFrame, IOleInPlaceUIWindow **ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect,
        LPOLEINPLACEFRAMEINFO lpFrameInfo);
    STDMETHOD(Scroll)(SIZE scrollExtant);
    STDMETHOD(OnUIDeactivate)(BOOL fUndoable);
    STDMETHOD(OnInPlaceDeactivate)(void);
    STDMETHOD(DiscardUndoState)(void);
    STDMETHOD(DeactivateAndUndo)(void);
    STDMETHOD(OnPosRectChange)(LPCRECT lprcPosRect);

    // IOleWindow
    STDMETHOD(GetWindow)(HWND *phwnd);
    STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode);

    // IOleControlSite
    STDMETHOD(OnControlInfoChanged)(void);
    STDMETHOD(LockInPlaceActive)(BOOL fLock);
    STDMETHOD(GetExtendedControl)(IDispatch **ppDisp);
    STDMETHOD(TransformCoords)(POINTL *pPtlHimetric, POINTF *pPtfContainer, DWORD dwFlags);
    STDMETHOD(TranslateAccelerator)(MSG *pMsg, DWORD grfModifiers);
    STDMETHOD(OnFocus)(BOOL fGotFocus);
    STDMETHOD(ShowPropertyFrame)(void);

    // IOleContainer
    STDMETHOD(EnumObjects)(DWORD grfFlags, IEnumUnknown **ppenum);
    STDMETHOD(LockContainer)(BOOL fLock);

    // IParseDisplayName
    STDMETHOD(ParseDisplayName)(IBindCtx *pbc, LPOLESTR pszDisplayName, ULONG *pchEaten, IMoniker **ppmkOut);

protected:
    HRESULT CreateActiveXWnd();

protected:
    LONG m_dwRef;
    CActiveXUI *m_pOwner;
    CActiveXWnd *m_pWindow;
    IUnknown *m_pUnkSite;
    IViewObject *m_pViewObject;
    IOleInPlaceObjectWindowless *m_pInPlaceObject;
    bool m_bLocked;
    bool m_bFocused;
    bool m_bCaptured;
    bool m_bUIActivated;
    bool m_bInPlaceActive;
    bool m_bWindowless;
};

CActiveXCtrl::CActiveXCtrl()
    : m_dwRef(1),
      m_pOwner(nullptr),
      m_pWindow(nullptr),
      m_pUnkSite(nullptr),
      m_pViewObject(nullptr),
      m_pInPlaceObject(nullptr),
      m_bLocked(false),
      m_bFocused(false),
      m_bCaptured(false),
      m_bWindowless(true),
      m_bUIActivated(false),
      m_bInPlaceActive(false)
{}

CActiveXCtrl::~CActiveXCtrl()
{
    if (m_pWindow != nullptr) {
        ::DestroyWindow(*m_pWindow);
        delete m_pWindow;
        m_pWindow = nullptr;
    }
    if (m_pUnkSite != nullptr) {
        m_pUnkSite->Release();
    }
    if (m_pViewObject != nullptr) {
        m_pViewObject->Release();
    }
    if (m_pInPlaceObject != nullptr) {
        m_pInPlaceObject->Release();
    }
}

STDMETHODIMP CActiveXCtrl::QueryInterface(REFIID riid, LPVOID *ppvObject)
{
    if (ppvObject == nullptr) {
        return E_NOINTERFACE;
    }
    *ppvObject = nullptr;
    if (riid == IID_IUnknown) {
        *ppvObject = static_cast<IOleWindow *>(this);
    } else if (riid == IID_IOleClientSite) {
        *ppvObject = static_cast<IOleClientSite *>(this);
    } else if (riid == IID_IOleInPlaceSiteWindowless) {
        *ppvObject = static_cast<IOleInPlaceSiteWindowless *>(this);
    } else if (riid == IID_IOleInPlaceSiteEx) {
        *ppvObject = static_cast<IOleInPlaceSiteEx *>(this);
    } else if (riid == IID_IOleInPlaceSite) {
        *ppvObject = static_cast<IOleInPlaceSite *>(this);
    } else if (riid == IID_IOleWindow) {
        *ppvObject = static_cast<IOleWindow *>(this);
    } else if (riid == IID_IOleControlSite) {
        *ppvObject = static_cast<IOleControlSite *>(this);
    } else if (riid == IID_IOleContainer) {
        *ppvObject = static_cast<IOleContainer *>(this);
    } else if (riid == IID_IObjectWithSite) {
        *ppvObject = static_cast<IObjectWithSite *>(this);
    }
    if (*ppvObject != nullptr) {
        AddRef();
    }
    return *ppvObject == nullptr ? E_NOINTERFACE : S_OK;
}

STDMETHODIMP_(ULONG)CActiveXCtrl::AddRef()
{
    return ++m_dwRef;
}

STDMETHODIMP_(ULONG)CActiveXCtrl::Release()
{
    LONG lRef = --m_dwRef;
    if (lRef == 0) {
        delete this;
    }
    return lRef;
}

STDMETHODIMP CActiveXCtrl::SetSite(IUnknown *pUnkSite)
{
    DUITRACE(_T("AX: CActiveXCtrl::SetSite"));
    if (m_pUnkSite != nullptr) {
        m_pUnkSite->Release();
        m_pUnkSite = nullptr;
    }
    m_pUnkSite = pUnkSite;
    if (m_pUnkSite != nullptr) {
        m_pUnkSite->AddRef();
    }
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::GetSite(REFIID riid, LPVOID *ppvSite)
{
    DUITRACE(_T("AX: CActiveXCtrl::GetSite"));
    if (ppvSite == nullptr) {
        return E_POINTER;
    }
    *ppvSite = nullptr;
    if (m_pUnkSite == nullptr) {
        return E_FAIL;
    }
    return m_pUnkSite->QueryInterface(riid, ppvSite);
}

STDMETHODIMP CActiveXCtrl::SaveObject(void)
{
    DUITRACE(_T("AX: CActiveXCtrl::SaveObject"));
    return E_NOTIMPL;
}

STDMETHODIMP CActiveXCtrl::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker **ppmk)
{
    DUITRACE(_T("AX: CActiveXCtrl::GetMoniker"));
    if (ppmk != nullptr) {
        *ppmk = nullptr;
    }
    return E_NOTIMPL;
}

STDMETHODIMP CActiveXCtrl::GetContainer(IOleContainer **ppContainer)
{
    DUITRACE(_T("AX: CActiveXCtrl::GetContainer"));
    if (ppContainer == nullptr) {
        return E_POINTER;
    }
    *ppContainer = nullptr;
    HRESULT Hr = E_NOTIMPL;
    if (m_pUnkSite != nullptr) {
        Hr = m_pUnkSite->QueryInterface(IID_IOleContainer, reinterpret_cast<LPVOID *>(ppContainer));
    }
    if (FAILED(Hr)) {
        Hr = QueryInterface(IID_IOleContainer, reinterpret_cast<LPVOID *>(ppContainer));
    }
    return Hr;
}

STDMETHODIMP CActiveXCtrl::ShowObject(void)
{
    DUITRACE(_T("AX: CActiveXCtrl::ShowObject"));
    if (m_pOwner == nullptr || m_pOwner->m_hwndHost == nullptr) {
        return E_UNEXPECTED;
    }
    HDC hDC = ::GetDC(m_pOwner->m_hwndHost);
    if (hDC == nullptr) {
        return E_FAIL;
    }
    if (m_pViewObject != nullptr) {
        m_pViewObject->Draw(DVASPECT_CONTENT, -1, nullptr, nullptr, nullptr, hDC,
            reinterpret_cast<RECTL *>(&m_pOwner->m_rcItem),
            reinterpret_cast<RECTL *>(&m_pOwner->m_rcItem),
            nullptr, NULL);
    }
    ::ReleaseDC(m_pOwner->m_hwndHost, hDC);
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::OnShowWindow(BOOL fShow)
{
    DUITRACE(_T("AX: CActiveXCtrl::OnShowWindow"));
    return E_NOTIMPL;
}

STDMETHODIMP CActiveXCtrl::RequestNewObjectLayout(void)
{
    DUITRACE(_T("AX: CActiveXCtrl::RequestNewObjectLayout"));
    return E_NOTIMPL;
}

STDMETHODIMP CActiveXCtrl::CanWindowlessActivate(void)
{
    DUITRACE(_T("AX: CActiveXCtrl::CanWindowlessActivate"));
    return S_OK; // Yes, we can!!
}

STDMETHODIMP CActiveXCtrl::GetCapture(void)
{
    DUITRACE(_T("AX: CActiveXCtrl::GetCapture"));
    if (m_pOwner == nullptr) {
        return E_UNEXPECTED;
    }
    return m_bCaptured ? S_OK : S_FALSE;
}

STDMETHODIMP CActiveXCtrl::SetCapture(BOOL fCapture)
{
    DUITRACE(_T("AX: CActiveXCtrl::SetCapture"));
    if (m_pOwner == nullptr || m_pOwner->m_hwndHost == nullptr) {
        return E_UNEXPECTED;
    }
    m_bCaptured = (fCapture == TRUE);
    if (fCapture) {
        ::SetCapture(m_pOwner->m_hwndHost);
    } else {
        ::ReleaseCapture();
    }
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::GetFocus(void)
{
    DUITRACE(_T("AX: CActiveXCtrl::GetFocus"));
    if (m_pOwner == nullptr) {
        return E_UNEXPECTED;
    }
    return m_bFocused ? S_OK : S_FALSE;
}

STDMETHODIMP CActiveXCtrl::SetFocus(BOOL fFocus)
{
    DUITRACE(_T("AX: CActiveXCtrl::SetFocus"));
    if (m_pOwner == nullptr) {
        return E_UNEXPECTED;
    }
    if (fFocus) {
        m_pOwner->SetFocus();
    }
    m_bFocused = (fFocus == TRUE);
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::GetDC(LPCRECT pRect, DWORD grfFlags, HDC *phDC)
{
    DUITRACE(_T("AX: CActiveXCtrl::GetDC"));
    if (phDC == nullptr) {
        return E_POINTER;
    }
    if (m_pOwner == nullptr || m_pOwner->m_hwndHost == nullptr) {
        return E_UNEXPECTED;
    }
    if (m_bWindowless) {
        return S_FALSE;
    }
    *phDC = ::GetDC(m_pOwner->m_hwndHost);
    if ((grfFlags & OLEDC_PAINTBKGND) != 0) {
        CDuiRect rcItem = m_pOwner->GetPos();
        if (!m_bWindowless) {
            rcItem.ResetOffset();
        }
        ::FillRect(*phDC, &rcItem, (HBRUSH)(COLOR_WINDOW + 1));
    }
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::ReleaseDC(HDC hDC)
{
    DUITRACE(_T("AX: CActiveXCtrl::ReleaseDC"));
    if (m_pOwner == nullptr || m_pOwner->m_hwndHost == nullptr) {
        return E_UNEXPECTED;
    }
    ::ReleaseDC(m_pOwner->m_hwndHost, hDC);
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::InvalidateRect(LPCRECT pRect, BOOL fErase)
{
    DUITRACE(_T("AX: CActiveXCtrl::InvalidateRect"));
    if (m_pOwner == nullptr) {
        return E_UNEXPECTED;
    }
    if (m_pOwner->m_hwndHost == nullptr) {
        return E_FAIL;
    }
    return ::InvalidateRect(m_pOwner->m_hwndHost, pRect, fErase) ? S_OK : E_FAIL;
}

STDMETHODIMP CActiveXCtrl::InvalidateRgn(HRGN hRGN, BOOL fErase)
{
    DUITRACE(_T("AX: CActiveXCtrl::InvalidateRgn"));
    if (m_pOwner == nullptr || m_pOwner->m_hwndHost == nullptr) {
        return E_UNEXPECTED;
    }
    return ::InvalidateRgn(m_pOwner->m_hwndHost, hRGN, fErase) ? S_OK : E_FAIL;
}

STDMETHODIMP CActiveXCtrl::ScrollRect(INT dx, INT dy, LPCRECT pRectScroll, LPCRECT pRectClip)
{
    DUITRACE(_T("AX: CActiveXCtrl::ScrollRect"));
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::AdjustRect(LPRECT prc)
{
    DUITRACE(_T("AX: CActiveXCtrl::AdjustRect"));
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::OnDefWindowMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
{
    DUITRACE(_T("AX: CActiveXCtrl::OnDefWindowMessage"));
    if (m_pOwner == nullptr || m_pOwner->m_hwndHost == nullptr) {
        return E_UNEXPECTED;
    }
    if (plResult == nullptr) {
        return E_UNEXPECTED;
    }
    *plResult = ::DefWindowProc(m_pOwner->m_hwndHost, msg, wParam, lParam);
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::OnInPlaceActivateEx(BOOL *pfNoRedraw, DWORD dwFlags)
{
    DUITRACE(_T("AX: CActiveXCtrl::OnInPlaceActivateEx"));
    ASSERT(m_pInPlaceObject == nullptr);
    if (m_pOwner == nullptr) {
        return E_UNEXPECTED;
    }
    if (m_pOwner->m_pUnk == nullptr)
        return E_UNEXPECTED;
    ::OleLockRunning(m_pOwner->m_pUnk, TRUE, FALSE);
    HWND hWndFrame = m_pOwner->GetManager()->GetPaintWindow();
    HRESULT Hr = E_FAIL;
    if ((dwFlags & ACTIVATE_WINDOWLESS) != 0) {
        m_bWindowless = true;
        Hr = m_pOwner->m_pUnk->QueryInterface(IID_IOleInPlaceObjectWindowless,
            reinterpret_cast<LPVOID *>(&m_pInPlaceObject));
        m_pOwner->m_hwndHost = hWndFrame;
        m_pOwner->GetManager()->AddMessageFilter(m_pOwner);
    }
    if (FAILED(Hr)) {
        m_bWindowless = false;
        Hr = CreateActiveXWnd();
        if (FAILED(Hr)) {
            return Hr;
        }
        Hr = m_pOwner->m_pUnk->QueryInterface(IID_IOleInPlaceObject, reinterpret_cast<LPVOID *>(&m_pInPlaceObject));
    }
    if (m_pInPlaceObject != nullptr && !m_pOwner->IsMFC()) {
        CDuiRect rcItem = m_pOwner->m_rcItem;
        if (!m_bWindowless) {
            rcItem.ResetOffset();
        }
        m_pInPlaceObject->SetObjectRects(&rcItem, &rcItem);
    }
    m_bInPlaceActive = SUCCEEDED(Hr);
    return Hr;
}

STDMETHODIMP CActiveXCtrl::OnInPlaceDeactivateEx(BOOL fNoRedraw)
{
    DUITRACE(_T("AX: CActiveXCtrl::OnInPlaceDeactivateEx"));
    m_bInPlaceActive = false;
    if (m_pInPlaceObject != nullptr) {
        m_pInPlaceObject->Release();
        m_pInPlaceObject = nullptr;
    }
    if (m_pWindow != nullptr) {
        ::DestroyWindow(*m_pWindow);
        delete m_pWindow;
        m_pWindow = nullptr;
    }
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::RequestUIActivate(void)
{
    DUITRACE(_T("AX: CActiveXCtrl::RequestUIActivate"));
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::CanInPlaceActivate(void)
{
    DUITRACE(_T("AX: CActiveXCtrl::CanInPlaceActivate"));
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::OnInPlaceActivate(void)
{
    DUITRACE(_T("AX: CActiveXCtrl::OnInPlaceActivate"));
    BOOL bDummy = FALSE;
    return OnInPlaceActivateEx(&bDummy, 0);
}

STDMETHODIMP CActiveXCtrl::OnUIActivate(void)
{
    DUITRACE(_T("AX: CActiveXCtrl::OnUIActivate"));
    m_bUIActivated = true;
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::GetWindowContext(IOleInPlaceFrame **ppFrame, IOleInPlaceUIWindow **ppDoc, LPRECT lprcPosRect,
    LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
    DUITRACE(_T("AX: CActiveXCtrl::GetWindowContext"));
    if (ppDoc == nullptr) {
        return E_POINTER;
    }
    if (ppFrame == nullptr) {
        return E_POINTER;
    }
    if (lprcPosRect == nullptr) {
        return E_POINTER;
    }
    if (lprcClipRect == nullptr) {
        return E_POINTER;
    }
    if (m_pWindow) {
        ::GetClientRect(m_pWindow->GetHWND(), lprcPosRect);
        ::GetClientRect(m_pWindow->GetHWND(), lprcClipRect);
    }
    *ppFrame = new CActiveXFrameWnd(m_pOwner);
    *ppDoc = nullptr;
    ACCEL ac = { 0 };
    HACCEL hac = ::CreateAcceleratorTable(&ac, 1);
    if (lpFrameInfo == nullptr) {
        return E_POINTER;
    }
    lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO);
    lpFrameInfo->fMDIApp = FALSE;
    lpFrameInfo->hwndFrame = m_pOwner->GetManager()->GetPaintWindow();
    lpFrameInfo->haccel = hac;
    lpFrameInfo->cAccelEntries = 1;
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::Scroll(SIZE scrollExtant)
{
    DUITRACE(_T("AX: CActiveXCtrl::Scroll"));
    return E_NOTIMPL;
}

STDMETHODIMP CActiveXCtrl::OnUIDeactivate(BOOL fUndoable)
{
    DUITRACE(_T("AX: CActiveXCtrl::OnUIDeactivate"));
    m_bUIActivated = false;
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::OnInPlaceDeactivate(void)
{
    DUITRACE(_T("AX: CActiveXCtrl::OnInPlaceDeactivate"));
    return OnInPlaceDeactivateEx(TRUE);
}

STDMETHODIMP CActiveXCtrl::DiscardUndoState(void)
{
    DUITRACE(_T("AX: CActiveXCtrl::DiscardUndoState"));
    return E_NOTIMPL;
}

STDMETHODIMP CActiveXCtrl::DeactivateAndUndo(void)
{
    DUITRACE(_T("AX: CActiveXCtrl::DeactivateAndUndo"));
    return E_NOTIMPL;
}

STDMETHODIMP CActiveXCtrl::OnPosRectChange(LPCRECT lprcPosRect)
{
    DUITRACE(_T("AX: CActiveXCtrl::OnPosRectChange"));
    return E_NOTIMPL;
}

STDMETHODIMP CActiveXCtrl::GetWindow(HWND *phwnd)
{
    DUITRACE(_T("AX: CActiveXCtrl::GetWindow"));
    if (m_pOwner == nullptr) {
        return E_UNEXPECTED;
    }
    if (m_pOwner->m_hwndHost == nullptr) {
        CreateActiveXWnd();
    }
    if (m_pOwner->m_hwndHost == nullptr) {
        return E_FAIL;
    }
    if (phwnd == nullptr) {
        return E_FAIL;
    }
    *phwnd = m_pOwner->m_hwndHost;
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::ContextSensitiveHelp(BOOL fEnterMode)
{
    DUITRACE(_T("AX: CActiveXCtrl::ContextSensitiveHelp"));
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::OnControlInfoChanged(void)
{
    DUITRACE(_T("AX: CActiveXCtrl::OnControlInfoChanged"));
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::LockInPlaceActive(BOOL fLock)
{
    DUITRACE(_T("AX: CActiveXCtrl::LockInPlaceActive"));
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::GetExtendedControl(IDispatch **ppDisp)
{
    DUITRACE(_T("AX: CActiveXCtrl::GetExtendedControl"));
    if (ppDisp == nullptr) {
        return E_POINTER;
    }
    if (m_pOwner == nullptr) {
        return E_UNEXPECTED;
    }
    if (m_pOwner->m_pUnk == nullptr) {
        return E_UNEXPECTED;
    }
    return m_pOwner->m_pUnk->QueryInterface(IID_IDispatch, reinterpret_cast<LPVOID *>(ppDisp));
}

STDMETHODIMP CActiveXCtrl::TransformCoords(POINTL *pPtlHimetric, POINTF *pPtfContainer, DWORD dwFlags)
{
    DUITRACE(_T("AX: CActiveXCtrl::TransformCoords"));
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::TranslateAccelerator(MSG *pMsg, DWORD grfModifiers)
{
    DUITRACE(_T("AX: CActiveXCtrl::TranslateAccelerator"));
    return S_FALSE;
}

STDMETHODIMP CActiveXCtrl::OnFocus(BOOL fGotFocus)
{
    DUITRACE(_T("AX: CActiveXCtrl::OnFocus"));
    m_bFocused = (fGotFocus == TRUE);
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::ShowPropertyFrame(void)
{
    DUITRACE(_T("AX: CActiveXCtrl::ShowPropertyFrame"));
    return E_NOTIMPL;
}

STDMETHODIMP CActiveXCtrl::EnumObjects(DWORD grfFlags, IEnumUnknown **ppenum)
{
    DUITRACE(_T("AX: CActiveXCtrl::EnumObjects"));
    if (ppenum == nullptr) {
        return E_POINTER;
    }
    if (m_pOwner == nullptr || m_pOwner->m_pUnk == nullptr) {
        return E_UNEXPECTED;
    }
    *ppenum = new CActiveXEnum(m_pOwner->m_pUnk);
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::LockContainer(BOOL fLock)
{
    DUITRACE(_T("AX: CActiveXCtrl::LockContainer"));
    m_bLocked = fLock != FALSE;
    return S_OK;
}

STDMETHODIMP CActiveXCtrl::ParseDisplayName(IBindCtx *pbc, LPOLESTR pszDisplayName, ULONG *pchEaten, IMoniker **ppmkOut)
{
    DUITRACE(_T("AX: CActiveXCtrl::ParseDisplayName"));
    return E_NOTIMPL;
}

HRESULT CActiveXCtrl::CreateActiveXWnd()
{
    if (m_pWindow != nullptr) {
        return S_OK;
    }
    m_pWindow = new CActiveXWnd;
    if (m_pWindow == nullptr) {
        return E_OUTOFMEMORY;
    }
    if (m_pOwner == nullptr) {
        return E_POINTER;
    }
    m_pOwner->m_hwndHost = m_pWindow->Init(this, m_pOwner->GetManager()->GetPaintWindow());
    return S_OK;
}

HWND CActiveXWnd::Init(CActiveXCtrl *pOwner, HWND hWndParent)
{
    m_pOwner = pOwner;
    UINT uStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
    Create(hWndParent, _T("UIActiveX"), uStyle, 0L, 0, 0, 0, 0, nullptr);
    return m_hWnd;
}

LPCTSTR CActiveXWnd::GetWindowClassName() const
{
    return _T("ActiveXWnd");
}

void CActiveXWnd::OnFinalMessage(HWND hWnd)
{
    if (m_pOwner == nullptr || m_pOwner->m_pOwner == nullptr || m_pOwner->m_pOwner->GetManager() == nullptr) {
        return;
    }
    if (m_pOwner->m_pOwner->GetManager()->IsLayered()) {
        m_pOwner->m_pOwner->GetManager()->RemovePaintChildWnd(hWnd);
    }
}

void CActiveXWnd::DoVerb(LONG iVerb)
{
    if (m_pOwner == nullptr) {
        return;
    }
    if (m_pOwner->m_pOwner == nullptr) {
        return;
    }
    IOleObject *pUnk = nullptr;
    m_pOwner->m_pOwner->GetControl(IID_IOleObject, reinterpret_cast<LPVOID *>(&pUnk));
    if (pUnk == nullptr) {
        return;
    }
    CSafeRelease<IOleObject> RefOleObject = pUnk;
    IOleClientSite *pOleClientSite = nullptr;
    m_pOwner->QueryInterface(IID_IOleClientSite, reinterpret_cast<LPVOID *>(&pOleClientSite));
    CSafeRelease<IOleClientSite> RefOleClientSite = pOleClientSite;
    pUnk->DoVerb(iVerb, nullptr, pOleClientSite, 0, m_hWnd, &m_pOwner->m_pOwner->GetPos());
}

LRESULT CActiveXWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LRESULT lRes = 0;
    BOOL bHandled = TRUE;
    switch (uMsg) {
        case WM_CREATE:
            lRes = OnCreate(uMsg, wParam, lParam, bHandled);
            break;
        case WM_TIMER:
            lRes = OnTimer(uMsg, wParam, lParam, bHandled);
            break;
        case WM_PAINT:
            lRes = OnPaint(uMsg, wParam, lParam, bHandled);
            break;
        case WM_PRINT:
            lRes = OnPrint(uMsg, wParam, lParam, bHandled);
            break;
        case WM_SETFOCUS:
            lRes = OnSetFocus(uMsg, wParam, lParam, bHandled);
            break;
        case WM_KILLFOCUS:
            lRes = OnKillFocus(uMsg, wParam, lParam, bHandled);
            break;
        case WM_ERASEBKGND:
            lRes = OnEraseBkgnd(uMsg, wParam, lParam, bHandled);
            break;
        case WM_MOUSEACTIVATE:
            lRes = OnMouseActivate(uMsg, wParam, lParam, bHandled);
            break;
        case WM_MOUSEWHEEL:
            break;
        default:
            bHandled = FALSE;
    }
    if (!bHandled) {
        return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
    }
    return lRes;
}

LRESULT CActiveXWnd::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
    if (m_pOwner == nullptr || m_pOwner->m_pOwner == nullptr) {
        return 0;
    }
    if (m_pOwner->m_pOwner->GetManager()->IsLayered()) {
        ::SetCoalescableTimer(m_hWnd, CARET_TIMERID, GetCaretBlinkTime(), nullptr, TIMERV_DEFAULT_COALESCING);
    }
    return 0;
}

LRESULT CActiveXWnd::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
    if (m_pOwner == nullptr || m_pOwner->m_pOwner == nullptr) {
        return 0;
    }
    if (wParam == CARET_TIMERID) {
        if (m_pOwner->m_pOwner->GetManager()->IsLayered() && m_pOwner->m_pOwner->IsVisible()) {
            m_pOwner->m_pOwner->GetManager()->AddPaintChildWnd(m_hWnd);
            m_bDrawCaret = !m_bDrawCaret;
        }
        return 0;
    }
    bHandled = FALSE;
    return 0;
}

LRESULT CActiveXWnd::OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
    if (m_pOwner == nullptr) {
        return 0;
    }
    if (m_pOwner->m_pViewObject == nullptr) {
        bHandled = FALSE;
    }
    return 1;
}

LRESULT CActiveXWnd::OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
    IOleObject *pUnk = nullptr;
    if (m_pOwner == nullptr || m_pOwner->m_pOwner == nullptr) {
        return 0;
    }
    m_pOwner->m_pOwner->GetControl(IID_IOleObject, reinterpret_cast<LPVOID *>(&pUnk));
    if (pUnk == nullptr) {
        return 0;
    }
    CSafeRelease<IOleObject> RefOleObject = pUnk;
    DWORD dwMiscStatus = 0;
    pUnk->GetMiscStatus(DVASPECT_CONTENT, &dwMiscStatus);
    if ((dwMiscStatus & OLEMISC_NOUIACTIVATE) != 0) {
        return 0;
    }
    if (!m_pOwner->m_bInPlaceActive) {
        DoVerb(OLEIVERB_INPLACEACTIVATE);
    }
    bHandled = FALSE;
    return 0;
}

LRESULT CActiveXWnd::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
    bHandled = FALSE;
    if (m_pOwner == nullptr) {
        return 0;
    }
    m_pOwner->m_bFocused = true;
    if (!m_pOwner->m_bUIActivated) {
        DoVerb(OLEIVERB_UIACTIVATE);
    }
    return 0;
}

LRESULT CActiveXWnd::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
    bHandled = FALSE;
    if (m_pOwner == nullptr) {
        return 0;
    }
    m_pOwner->m_bFocused = false;
    return 0;
}

LRESULT CActiveXWnd::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
    PAINTSTRUCT ps = { 0 };
    ::BeginPaint(m_hWnd, &ps);
    ::EndPaint(m_hWnd, &ps);
    return 1;
}

LRESULT CActiveXWnd::OnPrint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
    RECT rcClient;
    ::GetClientRect(m_hWnd, &rcClient);
    if (m_pOwner == nullptr || m_pOwner->m_pViewObject == nullptr) {
        return 0;
    }
    m_pOwner->m_pViewObject->Draw(DVASPECT_CONTENT, -1, nullptr, nullptr, nullptr, (HDC)wParam,
        reinterpret_cast<RECTL *>(&rcClient), nullptr, nullptr, NULL);
    // 当前界面不在最顶层时不画光标
    bool isTopWindow = true;
    if (m_pOwner != nullptr && m_pOwner->m_pOwner != nullptr && m_pOwner->m_pOwner->GetManager() != nullptr &&
        m_pOwner->m_pOwner->GetManager()->GetPaintWindow() != nullptr &&
        m_pOwner->m_pOwner->GetManager()->GetPaintWindow() != ::GetForegroundWindow()) {
        isTopWindow = false;
    }
    const int nSize = 2;
    if (m_bDrawCaret && isTopWindow) {
        RECT rcPos = m_pOwner->m_pOwner->GetPos();
        GUITHREADINFO guiThreadInfo = { 0 };
        guiThreadInfo.cbSize = sizeof(GUITHREADINFO);
        ::GetGUIThreadInfo(NULL, &guiThreadInfo);
        if (guiThreadInfo.hwndCaret) {
            POINT ptCaret;
            ptCaret.x = guiThreadInfo.rcCaret.left;
            ptCaret.y = guiThreadInfo.rcCaret.top;
            ::ClientToScreen(guiThreadInfo.hwndCaret, &ptCaret);
            ::ScreenToClient(m_pOwner->m_pOwner->GetManager()->GetPaintWindow(), &ptCaret);
            if (::PtInRect(&rcPos, ptCaret)) {
                RECT rcCaret;
                SecureZeroMemory(&rcCaret, sizeof(rcCaret));
                rcCaret = guiThreadInfo.rcCaret;
                rcCaret.right = rcCaret.left;
                if (rcCaret.bottom - rcCaret.top > 1) {
                    CRenderEngine::DrawLine((HDC)wParam, rcCaret, nSize, 0xFF000000);
                }
            }
        }
    }

    return 1;
}

IMPLEMENT_DUICONTROL(CActiveXUI)

CActiveXUI::CActiveXUI()
    : m_pUnk(nullptr), m_pControl(nullptr), m_hwndHost(nullptr), m_bCreated(false), m_bDelayCreate(true), m_bMFC(false)
{
    m_clsid = IID_NULL;
}

CActiveXUI::~CActiveXUI()
{
    ReleaseControl();
}

LPCTSTR CActiveXUI::GetClass() const
{
    return _T("ActiveXUI");
}

LPVOID CActiveXUI::GetInterface(LPCTSTR pstrName)
{
    if (_tcscmp(pstrName, DUI_CTR_ACTIVEX) == 0) {
        return static_cast<CActiveXUI *>(this);
    }
    return CControlUI::GetInterface(pstrName);
}

HWND CActiveXUI::GetHostWindow() const
{
    return m_hwndHost;
}

static void PixelToHiMetric(const SIZEL *lpSizeInPix, LPSIZEL lpSizeInHiMetric)
{
constexpr int HIMETRIC_PER_INCH = 2540;
#define MAP_PIX_TO_LOGHIM(x, ppli) MulDiv(HIMETRIC_PER_INCH, (x), (ppli))
#define MAP_LOGHIM_TO_PIX(x, ppli) MulDiv((ppli), (x), HIMETRIC_PER_INCH)
    int nPixelsPerInchX; // Pixels per logical inch along width
    int nPixelsPerInchY; // Pixels per logical inch along height
    HDC hDCScreen = ::GetDC(nullptr);
    nPixelsPerInchX = ::GetDeviceCaps(hDCScreen, LOGPIXELSX);
    nPixelsPerInchY = ::GetDeviceCaps(hDCScreen, LOGPIXELSY);
    ::ReleaseDC(nullptr, hDCScreen);
    lpSizeInHiMetric->cx = MAP_PIX_TO_LOGHIM(lpSizeInPix->cx, nPixelsPerInchX);
    lpSizeInHiMetric->cy = MAP_PIX_TO_LOGHIM(lpSizeInPix->cy, nPixelsPerInchY);
}

void CActiveXUI::SetVisible(bool bVisible)
{
    CControlUI::SetVisible(bVisible);
    if (m_pControl == nullptr) {
        return;
    }
    if (m_hwndHost != nullptr && !m_pControl->m_bWindowless) {
        ::ShowWindow(m_hwndHost, IsVisible() ? SW_SHOW : SW_HIDE);
    }
}

void CActiveXUI::SetInternVisible(bool bVisible)
{
    CControlUI::SetInternVisible(bVisible);
    if (m_pControl == nullptr) {
        return;
    }
    if (m_hwndHost != nullptr && !m_pControl->m_bWindowless) {
        ::ShowWindow(m_hwndHost, IsVisible() ? SW_SHOW : SW_HIDE);
    }
}

void CActiveXUI::SetPos(RECT rc, bool bNeedInvalidate)
{
    CControlUI::SetPos(rc, bNeedInvalidate);
    if (!m_bCreated) {
        DoCreateControl();
    }
    if (m_pUnk == nullptr) {
        return;
    }
    SetInternVisible(false);
    SIZEL hmSize = { 0 };
    SIZEL pxSize = { 0 };
    pxSize.cx = m_rcItem.right - m_rcItem.left;
    pxSize.cy = m_rcItem.bottom - m_rcItem.top;
    PixelToHiMetric(&pxSize, &hmSize);
    const int oneScale = 100;
    const int twoScale = 125;
    const int thirScale = 175;
    const int forScale = 200;
    const int fivScale = 225;
    const int sixScale = 250;
    if (m_pUnk != nullptr) {
        m_pUnk->SetExtent(DVASPECT_CONTENT, &hmSize);
        if (m_autoZoom) {
            SetZoomRate(CalZoomRate());
        }
    }
    if (m_pControl == nullptr || m_pControl->m_pWindow == nullptr) {
        return;
    }
    if (m_pControl->m_pInPlaceObject != nullptr) {
        CDuiRect rcItem = m_rcItem;
        if (!m_pControl->m_bWindowless) {
            rcItem.ResetOffset();
        }
        m_pControl->m_pInPlaceObject->SetObjectRects(&rcItem, &rcItem);
    }
    if (!m_pControl->m_bWindowless) {
        ASSERT(m_pControl->m_pWindow);
        ::MoveWindow(*m_pControl->m_pWindow, m_rcItem.left, m_rcItem.top, m_rcItem.right - m_rcItem.left,
            m_rcItem.bottom - m_rcItem.top, TRUE);
    }
    SetInternVisible(true);
}

void CActiveXUI::Move(SIZE szOffset, bool bNeedInvalidate)
{
    CControlUI::Move(szOffset, bNeedInvalidate);
    if (m_pControl == nullptr || m_pControl->m_pWindow == nullptr) {
        return;
    }
    if (!m_pControl->m_bWindowless) {
        ASSERT(m_pControl->m_pWindow);
        ::MoveWindow(*m_pControl->m_pWindow, m_rcItem.left, m_rcItem.top, m_rcItem.right - m_rcItem.left,
            m_rcItem.bottom - m_rcItem.top, TRUE);
    }
}

void CActiveXUI::DoPaint(HDC hDC, const RECT &rcPaint)
{
    if (!::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem)) {
        return;
    }

    if (m_pControl != nullptr && m_pControl->m_bWindowless && m_pControl->m_pViewObject != nullptr) {
        m_pControl->m_pViewObject->Draw(DVASPECT_CONTENT, -1, nullptr, nullptr, nullptr, hDC,
            reinterpret_cast<RECTL *>(&m_rcItem), reinterpret_cast<RECTL *>(&m_rcItem), nullptr, NULL);
    }
}

void CActiveXUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
{
    if (_tcscmp(pstrName, _T("clsid")) == 0) {
        CreateControl(pstrValue);
    } else if (_tcscmp(pstrName, _T("delaycreate")) == 0) {
        SetDelayCreate(_tcscmp(pstrValue, _T("true")) == 0);
    } else if (_tcscmp(pstrName, _T("mfc")) == 0) {
        SetMFC(_tcscmp(pstrValue, _T("true")) == 0);
    } else if (_tcscmp(pstrName, _T("autozoom")) == 0) {
        m_autoZoom = _tcscmp(pstrValue, _T("true")) == 0;
        if (!m_autoZoom) {
            SetZoomRate(m_oriZoomRatio);
        }
    } else if (_tcscmp(pstrName, _T("zoomratio")) == 0) {
        m_zoomRatio = _ttoi(pstrValue);
    } else {
        CControlUI::SetAttribute(pstrName, pstrValue);
    }
}

LRESULT CActiveXUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool &bHandled)
{
    if (m_pControl == nullptr) {
        return 0;
    }
    ASSERT(m_pControl->m_bWindowless);
    if (!m_pControl->m_bInPlaceActive) {
        return 0;
    }
    if (m_pControl->m_pInPlaceObject == nullptr) {
        return 0;
    }
    if (!IsMouseEnabled() && uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) {
        return 0;
    }
    bool bWasHandled = true;
    if ((uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) || uMsg == WM_SETCURSOR) {
        // Mouse message only go when captured or inside rect
        DWORD dwHitResult = m_pControl->m_bCaptured ? HITRESULT_HIT : HITRESULT_OUTSIDE;
        if (dwHitResult == HITRESULT_OUTSIDE && m_pControl->m_pViewObject != nullptr) {
            IViewObjectEx *pViewEx = nullptr;
            m_pControl->m_pViewObject->QueryInterface(IID_IViewObjectEx, reinterpret_cast<LPVOID *>(&pViewEx));
            if (pViewEx != nullptr) {
                POINT ptMouse = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
                pViewEx->QueryHitPoint(DVASPECT_CONTENT, &m_rcItem, ptMouse, 0, &dwHitResult);
                pViewEx->Release();
            }
        }
        if (dwHitResult != HITRESULT_HIT) {
            return 0;
        }
        if (uMsg == WM_SETCURSOR) {
            bWasHandled = false;
        }
    } else if (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST) {
        // Keyboard messages just go when we have focus
        if (!IsFocused()) {
            return 0;
        }
    } else {
        switch (uMsg) {
            case WM_HELP:
            case WM_CONTEXTMENU:
                bWasHandled = false;
                break;
            default:
                return 0;
        }
    }
    LRESULT lResult = 0;
    HRESULT Hr = m_pControl->m_pInPlaceObject->OnWindowMessage(uMsg, wParam, lParam, &lResult);
    if (Hr == S_OK) {
        bHandled = bWasHandled;
    }
    return lResult;
}

bool CActiveXUI::IsDelayCreate() const
{
    return m_bDelayCreate;
}

void CActiveXUI::SetDelayCreate(bool bDelayCreate)
{
    if (m_bDelayCreate == bDelayCreate) {
        return;
    }
    if (bDelayCreate == false) {
        if (m_bCreated == false && m_clsid != IID_NULL) {
            DoCreateControl();
        }
    }
    m_bDelayCreate = bDelayCreate;
}

bool CActiveXUI::IsMFC() const
{
    return m_bMFC;
}

void CActiveXUI::SetMFC(bool bMFC)
{
    if (m_bMFC == bMFC) {
        return;
    }
    m_bMFC = bMFC;
}

bool CActiveXUI::CreateControl(LPCTSTR pstrCLSID)
{
    const int iNum = 100;
    CLSID clsid = { 0 };
    OLECHAR szCLSID[iNum] = { 0 };
#ifndef _UNICODE
    ::MultiByteToWideChar(::GetACP(), 0, pstrCLSID, -1, szCLSID, lengthof(szCLSID) - 1);
#else
    if (pstrCLSID == nullptr) {
        return false;
    }
    _tcsncpy_s(szCLSID, pstrCLSID, lengthof(szCLSID) - 1);
#endif
    if (pstrCLSID[0] == '{') {
        ::CLSIDFromString(szCLSID, &clsid);
    } else {
        ::CLSIDFromProgID(szCLSID, &clsid);
    }
    return CreateControl(clsid);
}

bool CActiveXUI::CreateControl(const CLSID clsid)
{
    ASSERT(clsid != IID_NULL);
    if (clsid == IID_NULL) {
        return false;
    }
    m_bCreated = false;
    m_clsid = clsid;
    if (!m_bDelayCreate) {
        DoCreateControl();
    }
    return true;
}

void CActiveXUI::ReleaseControl()
{
    // 移除消息链
    if (m_pManager == nullptr) {
        return;
    }
    m_pManager->RemoveMessageFilter(this);

    if (m_pUnk != nullptr) {
        IObjectWithSite *pSite = nullptr;
        m_pUnk->QueryInterface(IID_IObjectWithSite, reinterpret_cast<LPVOID *>(&pSite));
        if (pSite != nullptr) {
            pSite->SetSite(nullptr);
            pSite->Release();
        }
        if (!IsMFC()) {
            m_pUnk->Close(OLECLOSE_NOSAVE);
        }
        m_pUnk->SetClientSite(nullptr);
        m_pUnk->Release();
        m_pUnk = nullptr;
    }
    // 销毁CActiveXCtrl
    if (m_pControl != nullptr) {
        m_pControl->m_pOwner = nullptr;
        m_pControl->Release();
        m_pControl = nullptr;
    }

    m_hwndHost = nullptr;
}

using DllGetClassObjectFunc = HRESULT(__stdcall *)(REFCLSID rclsid, REFIID riid, LPVOID *ppv);

bool CActiveXUI::DoCreateControl()
{
    ReleaseControl();
    // At this point we'll create the ActiveX control
    m_bCreated = true;
    IOleControl *pOleControl = nullptr;
    HRESULT Hr = -1;
    if (!m_sModuleName.IsEmpty()) {
        HMODULE hModule = ::LoadLibrary(m_sModuleName.GetData());
        if (hModule != nullptr) {
            IClassFactory *aClassFactory = nullptr;
            DllGetClassObjectFunc aDllGetClassObjectFunc =
                (DllGetClassObjectFunc)::GetProcAddress(hModule, "DllGetClassObject");
            if (aDllGetClassObjectFunc == nullptr) {
                ::FreeLibrary(hModule);
                return false;
            }
            Hr = aDllGetClassObjectFunc(m_clsid, IID_IClassFactory, reinterpret_cast<LPVOID *>(&aClassFactory));
            if (SUCCEEDED(Hr)) {
                Hr = aClassFactory->CreateInstance(nullptr, IID_IOleObject, reinterpret_cast<LPVOID *>(&pOleControl));
            }
            aClassFactory->Release();
            ::FreeLibrary(hModule);
        }
    }
    if (FAILED(Hr)) {
        Hr = ::CoCreateInstance(m_clsid, nullptr, CLSCTX_ALL, IID_IOleControl,
            reinterpret_cast<LPVOID *>(&pOleControl));
    }
    ASSERT(SUCCEEDED(Hr));
    if (FAILED(Hr)) {
        return false;
    }
    pOleControl->QueryInterface(IID_IOleObject, reinterpret_cast<LPVOID *>(&m_pUnk));
    pOleControl->Release();
    if (m_pUnk == nullptr) {
        return false;
    }
    // Create the host too
    m_pControl = new CActiveXCtrl();
    if (m_pControl == nullptr) {
        return false;
    }
    m_pControl->m_pOwner = this;
    // More control creation stuff
    DWORD dwMiscStatus = 0;
    m_pUnk->GetMiscStatus(DVASPECT_CONTENT, &dwMiscStatus);
    IOleClientSite *pOleClientSite = nullptr;
    m_pControl->QueryInterface(IID_IOleClientSite, reinterpret_cast<LPVOID *>(&pOleClientSite));
    CSafeRelease<IOleClientSite> RefOleClientSite = pOleClientSite;
    // Initialize control
    if ((dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST) != 0) {
        m_pUnk->SetClientSite(pOleClientSite);
    }
    IPersistStreamInit *pPersistStreamInit = nullptr;
    m_pUnk->QueryInterface(IID_IPersistStreamInit, reinterpret_cast<LPVOID *>(&pPersistStreamInit));
    if (pPersistStreamInit != nullptr) {
        Hr = pPersistStreamInit->InitNew();
        pPersistStreamInit->Release();
    }
    if (FAILED(Hr)) {
        return false;
    }
    if ((dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST) == 0) {
        m_pUnk->SetClientSite(pOleClientSite);
    }
    // Grab the view...
    Hr = m_pUnk->QueryInterface(IID_IViewObjectEx, reinterpret_cast<LPVOID *>(&m_pControl->m_pViewObject));
    if (FAILED(Hr)) {
        Hr = m_pUnk->QueryInterface(IID_IViewObject2, reinterpret_cast<LPVOID *>(&m_pControl->m_pViewObject));
    }
    if (FAILED(Hr)) {
        Hr = m_pUnk->QueryInterface(IID_IViewObject, reinterpret_cast<LPVOID *>(&m_pControl->m_pViewObject));
    }
    // Activate and done...
    m_pUnk->SetHostNames(OLESTR("UIActiveX"), nullptr);
    if (m_pManager == nullptr) {
        return false;
    }
    if (m_pManager != nullptr) {
        m_pManager->SendNotify(reinterpret_cast<CControlUI *>(this), DUI_MSGTYPE_SHOWACTIVEX, 0, 0, false);
    }
    if ((dwMiscStatus & OLEMISC_INVISIBLEATRUNTIME) == 0) {
        try {
            Hr = m_pUnk->DoVerb(OLEIVERB_INPLACEACTIVATE, nullptr, pOleClientSite, 0,
                m_pManager->GetPaintWindow(), &m_rcItem);
        } catch (...) {
        }
    }
    IObjectWithSite *pSite = nullptr;
    m_pUnk->QueryInterface(IID_IObjectWithSite, reinterpret_cast<LPVOID *>(&pSite));
    if (pSite != nullptr) {
        pSite->SetSite(static_cast<IOleClientSite *>(m_pControl));
        pSite->Release();
    }
    return SUCCEEDED(Hr);
}

HRESULT CActiveXUI::GetControl(const IID iid, LPVOID *ppRet)
{
    ASSERT(ppRet != nullptr);
    ASSERT(*ppRet == nullptr);
    if (ppRet == nullptr) {
        return E_POINTER;
    }
    if (m_pUnk == nullptr) {
        return E_PENDING;
    }
    return m_pUnk->QueryInterface(iid, reinterpret_cast<LPVOID *>(ppRet));
}

CLSID CActiveXUI::GetClisd() const
{
    return m_clsid;
}

CDuiString CActiveXUI::GetModuleName() const
{
    return m_sModuleName;
}

void CActiveXUI::SetModuleName(LPCTSTR pstrText)
{
    m_sModuleName = pstrText;
}

float CActiveXUI::CalZoomCoefficient()
{
    const std::map<int, float> scaleToRate = {
        {100, 0.9f},
        {125, 1.125f},
        {150, 1.35f},
        {175, 1.575f},
        {200, 1.8f},
        {225, 1.95f},
        {250, 2.0f},
    };

    bool scaleValid = true;
    UINT scaleTemp = 0;
    if (m_pManager == nullptr) {
        scaleValid = false;
    } else {
        scaleTemp = CResourceManager::GetInstance()->GetScale(*m_pManager);
    }

    float zoomRate = 1.0f;
    if (!scaleValid) {
        return zoomRate;
    }

    zoomRate = scaleToRate.rbegin()->second;
    for (auto &obj : scaleToRate) {
        if (scaleTemp <= obj.first) {
            zoomRate = obj.second;
            break;
        }
    }
    return zoomRate;
}

bool CActiveXUI::GetCurZoomRate(LONG &zoomRate)
{
    CComPtr<IWebBrowser2> webBrowser2;
    VARIANT varZoom;
    auto hr = GetControl(IID_IWebBrowser2, reinterpret_cast<void**>(&webBrowser2));
    if (!SUCCEEDED(hr) || (webBrowser2 == nullptr)) {
        return false;
    }
    auto ret = webBrowser2->ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DODEFAULT, nullptr, &varZoom);
    if (!SUCCEEDED(ret) || V_VT(&varZoom) != VT_I4) {
        return false;
    }
    zoomRate = varZoom.lVal;
    return true;
}

LONG CActiveXUI::CalZoomRate()
{
    LONG zoomRate = 100; // 默认缩放比
    do {
        if (!m_autoZoom) {
            (void)GetCurZoomRate(zoomRate);
            break;
        }

        // 不指定原始缩放，基于当今值进行缩放
        if (m_oriZoomRatio == 0) {
            (void)GetCurZoomRate(m_oriZoomRatio);
        }
        if (m_zoomRatio == 0) {
            zoomRate = static_cast<LONG>(m_oriZoomRatio * CalZoomCoefficient());
            break;
        }
        zoomRate = m_zoomRatio * CalZoomCoefficient();
    } while (false);
    return zoomRate;
}

void CActiveXUI::SetZoomRate(LONG scaleZoom)
{
    constexpr LONG minScaleZoom = 10;
    constexpr LONG maxScaleZoom = 1000;
    if (scaleZoom < minScaleZoom || scaleZoom > maxScaleZoom) {
        return;
    }
    CComPtr<IWebBrowser2> webBrowser2;
    auto hr = GetControl(IID_IWebBrowser2, reinterpret_cast<void**>(&webBrowser2));
    if (!SUCCEEDED(hr) || (webBrowser2 == nullptr)) {
        return;
    }
    VARIANT varZoomIn;
    varZoomIn.vt = VT_I4;
    varZoomIn.lVal = scaleZoom;
    webBrowser2->ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DODEFAULT, &varZoomIn, nullptr);
}

} // namespace DuiLib